package com.estiag.redistest;

import jakarta.annotation.Resource;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;
import java.util.concurrent.locks.ReentrantLock;

@RestController
public class RedisController {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RedissonClient redissonClient;
    ReentrantLock reentrantLock = new ReentrantLock();

    @GetMapping("/hi")
    public String hi() {
        return "hi";
    }

    /**
     * 扣减库存在单jvm情况下ok，分布式环境下有问题
     * @return
     */
    @GetMapping("/reduce")
    public int reduce() {
        reentrantLock.lock();
        try {
            int k1 = Integer.parseInt(stringRedisTemplate.opsForValue().get("k1"));
            int rest = 0;
            if (k1 > 0) {
                rest = k1 - 1;
                stringRedisTemplate.opsForValue().set("k1", String.valueOf(rest));
                System.out.println("剩余库存" + rest);
            }
            return rest;
        } catch (Exception e) {
            System.out.println(e);
            return 0;
        } finally {
            reentrantLock.unlock();
        }
    }

    /**
     * 最简单的分布式锁实现，底层setnx
     * 问题：业务代码抛异常时释放锁操作不会执行-死锁
     * 即使用finally解锁也无法解决jvm挂掉的锁释放
     * 解决：设置锁超时时间（设置key的同时设置超时时间）
     * 问题：锁超时后业务还没有执行完，锁会被自动释放，执行完后再次释放锁会释放别的线程的锁
     * 解决1：每个线程生成一个uuid判断是不是自己加的锁（也需要和释放组成原子操作）
     * 解决2：锁续命（另开一个线程判断主线程还在的话就续期）
     * @return
     */
    @GetMapping("/safeReduceLv1")
    public String safeReduceLv1() {
        String lockKey = "lock-k1";
        //设置超时时间需要和设置key的操作放在原子操作里
        Boolean lockSuccess = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "1", Duration.ofMinutes(10));
        if (!lockSuccess) {
            return "稍后再试";
        }
        try {
            int k1 = Integer.parseInt(stringRedisTemplate.opsForValue().get("k1"));
            int rest = 0;
            if (k1 > 0) {
                rest = k1 - 1;
                stringRedisTemplate.opsForValue().set("k1", String.valueOf(rest));
                System.out.println("剩余库存" + rest);
            }
            return String.valueOf(rest);
        } catch (Exception e) {
            return "出问题了";
        } finally {
            stringRedisTemplate.delete(lockKey);
        }
    }

    /**
     * 使用Redisson的分布式锁
     * @return
     */
    @GetMapping("/safeReduceLv2")
    public String safeReduceLv2() {
        String lockKey = "lock-k1";
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        int k1 = Integer.parseInt(stringRedisTemplate.opsForValue().get("k1"));
        int rest = 0;
        if (k1 > 0) {
            rest = k1 - 1;
            stringRedisTemplate.opsForValue().set("k1", String.valueOf(rest));
            System.out.println("剩余库存" + rest);
        }
        lock.unlock();
        return String.valueOf(rest);
    }


}
